<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/%E6%AD%A6%E6%B1%8932x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/%E6%AD%A6%E6%B1%8916x16.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib1/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="/lib1/pace/pace-theme-center-simple.min.css">
  <script src="/lib1/pace/pace.min.js"></script>

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"example.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="前言Rax 是阿里巴巴应用最广泛的跨端解决方案，支持开发者通过类 React DSL 编写 Web、小程序、Flutter 等不同容器的跨端应用。 与Rax有关的官方体系如下： Rax Core：支持多端的类React DSL；支持 80% 以上的 React API，比 React 更加轻量、渲染速度更快，同时通过 Driver 机制更好的支持跨多端渲染 Rax App：开箱即用的研发框架 Ra">
<meta property="og:type" content="article">
<meta property="og:title" content="Rax初学">
<meta property="og:url" content="http://example.com/2021/11/20/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Rax%E5%88%9D%E5%AD%A6/index.html">
<meta property="og:site_name" content="Technological Blog">
<meta property="og:description" content="前言Rax 是阿里巴巴应用最广泛的跨端解决方案，支持开发者通过类 React DSL 编写 Web、小程序、Flutter 等不同容器的跨端应用。 与Rax有关的官方体系如下： Rax Core：支持多端的类React DSL；支持 80% 以上的 React API，比 React 更加轻量、渲染速度更快，同时通过 Driver 机制更好的支持跨多端渲染 Rax App：开箱即用的研发框架 Ra">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://example.com/image/Rax%E5%88%9D%E5%AD%A6/start.png">
<meta property="og:image" content="http://example.com/image/Rax%E5%88%9D%E5%AD%A6/test.png">
<meta property="og:image" content="http://example.com/image/Rax%E5%88%9D%E5%AD%A6/build.png">
<meta property="article:published_time" content="2021-11-20T04:18:39.000Z">
<meta property="article:modified_time" content="2021-11-24T07:56:15.905Z">
<meta property="article:author" content="Li Yudong">
<meta property="article:tag" content="公司技术栈">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://example.com/image/Rax%E5%88%9D%E5%AD%A6/start.png">

<link rel="canonical" href="http://example.com/2021/11/20/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Rax%E5%88%9D%E5%AD%A6/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>Rax初学 | Technological Blog</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Technological Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">IT小白的成长之旅</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">18</span></a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">14</span></a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">95</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <a href="https://github.com/tiarmor1" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/20/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Rax%E5%88%9D%E5%AD%A6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Rax初学
        </h1>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-11-20 12:18:39" itemprop="dateCreated datePublished" datetime="2021-11-20T12:18:39+08:00">2021-11-20</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-24 15:56:15" itemprop="dateModified" datetime="2021-11-24T15:56:15+08:00">2021-11-24</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88/" itemprop="url" rel="index"><span itemprop="name">盒马技术栈</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
        <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>Rax 是阿里巴巴应用最广泛的跨端解决方案，支持开发者通过类 React DSL 编写 Web、小程序、Flutter 等不同容器的跨端应用。</p>
<p>与Rax有关的官方体系如下：</p>
<p>Rax Core：支持多端的类React DSL；支持 80% 以上的 React API，比 React 更加轻量、渲染速度更快，同时通过 Driver 机制更好的支持跨多端渲染</p>
<p>Rax App：开箱即用的研发框架 Rax App；提供项目研发的基础规范和最佳实践，包含目录规范、工程能力、MPA、SPA、SSR、状态管理、路由、埋点监控等能力。</p>
<p>Rax Miniapp：跨端小程序；使用完整的 Rax 语法编写跨多厂商的小程序，以运行时为主，局部支持编写编译时组件。</p>
<p>Rax Components：跨多端的基础元件；抹平多端差异的基础元件，比如 Text、View、Image 等。</p>
<p>Fusion Mobile：丰富的跨端组件；支持 Web 以及小程序，不支持 Weex、Kraken</p>
<p>Uni API：跨多端的 API；支持微信、支付宝等厂商小程序，部分 API 支持 Web，暂不支持 App 里的各种 jsbridge。</p>
<h2 id="与React区别"><a href="#与React区别" class="headerlink" title="与React区别"></a>与React区别</h2><p>Rax与React的区别：1、Rax 面向多端设计的，从最初始就引入了 Driver 机制来适配不同端，这在 React 的早期版本中是不支持的；2、相比 React 更加轻量，gzip 之后只有 6KB。</p>
<p>针对React的诸多API，Rax不支持 Suspense、lazy API，其他诸如 Hooks、Component 等 API 都支持。</p>
<p>Rax 在 DSL 层面与框架设计方面借鉴了 React 的部分思想，但是舍弃了 React 部分传统的 API，所以体积上更加轻量。Rax 推荐开发者使用函数组件（Function Component）配合 Hooks 开发，相比传统类组件（Class Components）是一种更轻量便捷的开发风格。</p>
<p>如果我们的旧项目是用React框架实现的，我们想要把React项目代码迁移为Rax代码，则可以通过以下的几种方法来实现：</p>
<h3 id="修改写法迁移"><a href="#修改写法迁移" class="headerlink" title="修改写法迁移"></a>修改写法迁移</h3><p>由于底层实现不同，所以Rax项目和React项目引入的依赖是不同的。</p>
<p>创建Rax元素：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在React中</span></span><br><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="comment">//在Rax中对应的修改</span></span><br><span class="line"><span class="keyword">import</span> &#123; createElement &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br></pre></td></tr></table></figure>

<p>元素渲染：</p>
<p>在 React 中，需要在项目入口调用 <code>ReactDOM.render()</code>，将 <code>React Element</code> 渲染到真实的 DOM 节点。Rax 与之不同的是，Rax 支持跨容器渲染，在 Rax 的项目中调用 render 方法的时候，我们需要指定对应容器使用的 <a href="https://github.com/alibaba/rax/wiki/Driver-Specification" target="_blank" rel="noopener">Driver</a> 。基于此，我们需要做出以下对应的修改：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在React中</span></span><br><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> ReactDOM <span class="keyword">from</span> <span class="string">'react-dom'</span>;</span><br><span class="line"></span><br><span class="line">ReactDOM.render(</span><br><span class="line">  &lt;h1&gt;Hello, world!&lt;<span class="regexp">/h1&gt;,</span></span><br><span class="line"><span class="regexp">  document.getElementById('root')</span></span><br><span class="line"><span class="regexp">);</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/在Rax中</span></span><br><span class="line"><span class="regexp">import &#123; createElement, render &#125; from 'rax';</span></span><br><span class="line"><span class="regexp">import * as DriverDOM from 'driver-dom';</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">render(</span></span><br><span class="line"><span class="regexp">  &lt;h1&gt;Hello, world!&lt;/</span>h1&gt;,</span><br><span class="line">  <span class="built_in">document</span>.body,</span><br><span class="line">  &#123; <span class="attr">driver</span>: DriverDOM &#125;</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<p>Webpack配置迁移：</p>
<p>在 Webpack 配置中，通过添加 react 和 react-dom 的 alias 来让使用 Rax 运行已有的 React 项目，这种方法不需要修改任何项目代码。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  resolve: &#123;</span><br><span class="line">    alias: &#123;</span><br><span class="line">      'react': 'rax/lib/compat',</span><br><span class="line">      'react-dom': 'rax-dom'</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过工程插件迁移</p>
<p>如果使用的是 Rax 官方的工程，可以通过添加工程插件 <code>build-plugin-rax-compat-react</code> 的方式实现兼容，无需修改代码。详见<a href="https://www.npmjs.com/package/build-plugin-rax-compat-react" target="_blank" rel="noopener">build-plugin-rax-compat-react</a>。在 <code>build.json</code> 中按照以下方式配置该插件即可：</p>
<p>Rax cli 在初始化项目时同样提供了兼容 React 的选项，可以在 <code>build.json</code> 中自动生成上述配置</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"plugins"</span>: [</span><br><span class="line">    <span class="string">"build-plugin-rax-compat-react"</span></span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>为了减小 Rax 的体积，让开发者更加专注与 Hooks 的开发方式，Rax 将部分 API 从 Core 中移了出来。但我们仍然可以通过引入独立包的方式使用它们，以 <code>React.findDOMNode</code> 为例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在React中</span></span><br><span class="line">ReactDOM.findDOMNode(component);</span><br><span class="line"></span><br><span class="line"><span class="comment">//在Rax中</span></span><br><span class="line"><span class="keyword">import</span> findDOMNode <span class="keyword">from</span> <span class="string">'rax-find-dom-node'</span>;</span><br><span class="line">findDOMNode(component);</span><br></pre></td></tr></table></figure>

<h1 id="Rax基础指南"><a href="#Rax基础指南" class="headerlink" title="Rax基础指南"></a>Rax基础指南</h1><h2 id="组件编写"><a href="#组件编写" class="headerlink" title="组件编写"></a>组件编写</h2><h3 id="Component"><a href="#Component" class="headerlink" title="Component"></a>Component</h3><p>组件，在概念上类似于 Javascript 函数。它接受从父级元素传入的数据（即 Props)，并返回用于描述页面展示内容的 Rax 元素。组件使得我们的UI界面分成独立的、可重用的部分，并且，每个部分可以单独维护。</p>
<p>定义组件有两种方式，分别为 Function Component, 和 Class Component。</p>
<p>Function Component 是定义组件最简单的方式，这种方式会让代码变得更加简洁，并且可以使用 Hooks 编写更易维护和扩展的组件。我们推荐用 Function Component 来定义组件：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createElement &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Welcome</span>(<span class="params">props</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello, &#123;props.name&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//下面是ES6的Class编写class组件的写法</span></span><br><span class="line"><span class="keyword">import</span> &#123; createElement, Component &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Welcome</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello, &#123;this.props.name&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>需要注意的是，Rax 组件在命名时首字母必须大写。首字母为小写的组件会被视为原生 DOM 标签。例如，<code>&lt;div /&gt;</code> 代表 HTML 的 div 标签，而 <code>&lt;Welcome /&gt;</code> 则代表一个 Rax 组件。</p>
<h4 id="Props"><a href="#Props" class="headerlink" title="Props"></a>Props</h4><p>Props 为组件接受的入参，通过 Props, 组件可以接收来自父级节点的数据。Props 都是只读的，不能在当前组件中修改 Props 的值。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createElement &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">App</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Welcome name=<span class="string">"Tom"</span> /&gt;</span><br><span class="line">      &lt;Welcome name=<span class="string">"Lily"</span> /&gt;</span><br><span class="line">      &lt;Welcome name=<span class="string">"Susan"</span> /&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">function Welcome(props) &#123;</span></span><br><span class="line"><span class="regexp">  return &lt;h1&gt;Hello, &#123;props.name&#125;&lt;/</span>h1&gt;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="State"><a href="#State" class="headerlink" title="State"></a>State</h4><p>State 是组件的私有变量，由组件自身控制，并与 Props 一起控制组件的渲染。我们使用 <code>useState</code> 这一 Hook 来进行定义和管理 State 。在下面的例子里，我们使用了 <code>useState</code> 来实现了一个计数器：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createElement, useState &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Example</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [count, setCount] = useState(<span class="number">0</span>);</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;p&gt;You clicked &#123;count&#125; times&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">      &lt;button onClick=&#123;() =&gt; setCount(count + 1)&#125;&gt;</span></span><br><span class="line"><span class="regexp">        Click me</span></span><br><span class="line"><span class="regexp">      &lt;/</span>button&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>

<p><code>useState</code> 参数是初始 state，会返回一个数组。数组第一个值是 state，第二个值是改变 state 的函数。上述例子中 count 与 setCount 是一一配对的，count 的值只能通过 setCount 修改。</p>
<h4 id="Fragment"><a href="#Fragment" class="headerlink" title="Fragment"></a>Fragment</h4><p>Fragment 是一个特殊组件，可以在不创建额外 DOM 元素的情况下，将多个元素组合在一起。在 rax 组件渲染时，若需要渲染多个元素，需要使用 Fragment 将元素包裹在内。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createElement, Fragment &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Hello</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;Fragment&gt;</span><br><span class="line">      &lt;h1&gt;Hello&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">      &lt;h1&gt;World&lt;/</span>h1&gt;</span><br><span class="line">    &lt;<span class="regexp">/Fragment&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>

<p>Fragment 组件只支持 <code>key</code> 属性 和 部分 JSX+ 属性即 <code>x-if</code> <code>x-for</code> <code>x-slot</code>，不支持其他属性。当 Fragment 组件不需要填写属性时，我们也可以使用<code>&lt;&gt;&lt;/&gt;</code>来代替。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createElement &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Hello</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;&gt;</span><br><span class="line">      &lt;h1&gt;Hello&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">      &lt;h1&gt;World&lt;/</span>h1&gt;</span><br><span class="line">    &lt;<span class="regexp">/&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>

<h3 id="Hooks"><a href="#Hooks" class="headerlink" title="Hooks"></a>Hooks</h3><p>Hooks 是 Rax 1.0 新增的特性，它可以让函数组件（Function Component）使用状态和生命周期。Rax 在实现上遵循了 <a href="https://reactjs.org/docs/hooks-intro.html" target="_blank" rel="noopener">React Hooks</a> 的标准</p>
<h4 id="useState"><a href="#useState" class="headerlink" title="useState"></a>useState</h4><p>useState 主要用来定义和管理本地状态。在下面的例子里，我们使用了 <code>useState</code> 来实现了一个计数器</p>
<p><code>useState</code> 参数是初始 state，会返回一个数组。数组第一个值是 state，第二个值是改变 state 的函数。上述例子中 count 与 setCount 是一一配对的，count 的值只能通过 setCount 修改。</p>
<h4 id="useEffect"><a href="#useEffect" class="headerlink" title="useEffect"></a>useEffect</h4><p>useEffect 的代码在每次渲染后运行，包括第一次渲染：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createElement, useState, useEffect &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Example</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [count, setCount] = useState(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Similar to componentDidMount and componentDidUpdate:</span></span><br><span class="line">  useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">document</span>.title = <span class="string">`You clicked <span class="subst">$&#123;count&#125;</span> times`</span>;</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;p&gt;You clicked &#123;count&#125; times&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">      &lt;button onClick=&#123;() =&gt; setCount(count + 1)&#125;&gt;</span></span><br><span class="line"><span class="regexp">        Click me</span></span><br><span class="line"><span class="regexp">      &lt;/</span>button&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>

<p><code>useEffect</code> 它跟类组件（Class Component）中的 <code>componentDidMount</code>、<code>componentDidUpdate</code> 和 <code>componentWillUnmount</code> 具有相同的用途，只不过被合并成了一个 API。</p>
<h4 id="Hooks-使用规则"><a href="#Hooks-使用规则" class="headerlink" title="Hooks 使用规则"></a>Hooks 使用规则</h4><p>Hooks 就是 JavaScript 函数，但是使用它们会有两个额外的规则：</p>
<ul>
<li>只在<strong>函数最顶层</strong>调用 Hooks，不要在循环、条件判断或者子函数中调用。</li>
<li>只在 <strong>Rax 函数组件</strong> 和 <strong>自定义 Hooks</strong> 中调用 Hooks，不要在其他 JavaScript 函数中调用。</li>
</ul>
<p>其实各类规则与React Hooks的规则与注意事项一样，熟悉React Hooks即可。</p>
<h3 id="JSX-语法"><a href="#JSX-语法" class="headerlink" title="JSX+语法"></a>JSX+语法</h3><p>Rax 支持了一种 JSX 扩展语法 JSX+，它能帮助业务开发者更爽更快地书写 JSX。JSX+ 不是一种新的概念，它是 JSX 基础上的扩展指令概念。具体语法如下：</p>
<p>1、条件判断</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;View x-<span class="keyword">if</span>=&#123;condition&#125;&gt;Hello&lt;<span class="regexp">/View&gt;</span></span><br><span class="line"><span class="regexp">&lt;View x-elseif=&#123;anotherCondition&#125;&gt;&lt;/</span>View&gt;</span><br><span class="line">&lt;View x-<span class="keyword">else</span>&gt;NothingElse&lt;<span class="regexp">/View&gt;</span></span><br></pre></td></tr></table></figure>

<p>注： <code>x-elseif</code> 可以多次出现，但是顺序必须是 x-if -&gt; x-elseif -&gt; x-else，且这些节点是兄弟节点关系，如顺序错误则指令被忽略</p>
<p>2、循环列表</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="comment">/* Array or Plain Object*/</span>&#125;</span><br><span class="line">&lt;tag x-<span class="keyword">for</span>=&#123;item <span class="keyword">in</span> foo&#125;&gt;&#123;item&#125;&lt;<span class="regexp">/tag&gt;</span></span><br><span class="line"><span class="regexp">&lt;tag x-for=&#123;(item, key) in foo&#125;&gt;&#123;key&#125;: &#123;item&#125;&lt;/</span>tag&gt;</span><br></pre></td></tr></table></figure>

<p>说明： 1. 若循环对象为数组，key 表示循环索引，其类型为 Number。 2. 当 <code>x-for</code> 与 <code>x-if</code> 同时作用在同一节点上时，循环优先级大于条件，即循环的 <code>item</code> 和 <code>index</code> 可以在子条件判断中使用。</p>
<p>3、单次渲染</p>
<p>仅在首次渲染时会触发 <code>createElement</code> 并将其引用缓存，re-render 时直接复用缓存，用于提高不带绑定节点渲染效率和 Diff 性能</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;p x-memo&gt;<span class="keyword">this</span> paragragh &#123;mesasge&#125; content will not change.&lt;<span class="regexp">/p&gt;</span></span><br></pre></td></tr></table></figure>

<p>4、插槽指令：类似于WebComponents 的 slot 概念，并提供插槽作用域。插槽用于父组件直接以插槽的形式，直接向子组件传递JSX元素，常用的有具名插槽与作用域插槽。</p>
<p>和Vue的插槽很像，在向具名插槽提供内容的时候，我们可以在一个 <code>&lt;view&gt;</code> 元素上使用 <code>x-slot</code> 指令，并以 <code>x-slot</code> 的参数的形式提供其名称；例如：header、footer。</p>
<p>作用域插槽：由于父级模板里的所有内容都是在父级作用域中编译的；子模板里的所有内容都是在子作用域中编译的。因此插槽中的信息无法访问子组件中数据，从而无法根据子组件数据更新而变化。因此有时让插槽内容能够访问子组件中才有的数据是很有用的。</p>
<p>如下例，为了让item在父级的插槽内容中可用，我们将item作为slot元素的一个属性绑定上去。绑定在slot元素上的属性被称为插槽prop，这样在父级作用域中，我们可以使用带值的 <code>x-slot</code> 来定义我们提供的插槽 prop 的名字：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">&lt;tag x-slot:slotName=<span class="string">"slotScope"</span> /&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Example</span></span><br><span class="line">&lt;Waterfall&gt;</span><br><span class="line">  &lt;view x-slot:header&gt;header&lt;<span class="regexp">/view&gt;</span></span><br><span class="line"><span class="regexp">  &lt;view x-slot:item="props"&gt;&#123;props.index&#125;: &#123;props.item&#125;&lt;/</span>view&gt;</span><br><span class="line">  &lt;view x-slot:footer&gt;footer&lt;<span class="regexp">/view&gt;</span></span><br><span class="line"><span class="regexp">&lt;/</span>Waterfall&gt;</span><br><span class="line"></span><br><span class="line">&lt;slot name=<span class="string">"header"</span> /&gt; <span class="comment">// 槽位</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//对比传统的JSX如下：传递给子组件回调函数，在子组件中进行调用</span></span><br><span class="line">&lt;Waterfall</span><br><span class="line">  renderHeader=&#123;() =&gt; (<span class="xml"><span class="tag">&lt;<span class="name">view</span>&gt;</span>header<span class="tag">&lt;/<span class="name">view</span>&gt;</span></span>)&#125;</span><br><span class="line">  renderFooter=&#123;() =&gt; (<span class="xml"><span class="tag">&lt;<span class="name">view</span>&gt;</span>footer<span class="tag">&lt;/<span class="name">view</span>&gt;</span></span>)&#125;</span><br><span class="line">  renderItem=&#123;(item, index) =&gt; (<span class="xml"><span class="tag">&lt;<span class="name">view</span>&gt;</span>&#123;index&#125;: &#123;item&#125;<span class="tag">&lt;/<span class="name">view</span>&gt;</span></span>&#125;</span><br><span class="line">/&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">//对比小程序的实现方式 slot-scope其实就是vue旧版本的实现方法。</span></span><br><span class="line">&lt;Waterfall&gt;</span><br><span class="line">  &lt;view slot=<span class="string">"header"</span>&gt;header&lt;<span class="regexp">/view&gt;</span></span><br><span class="line"><span class="regexp">  &lt;view slot="item" slot-scope="props"&gt;&#123;props.index&#125;: &#123;props.item&#125;&lt;/</span>view&gt;</span><br><span class="line">  &lt;view slot=<span class="string">"footer"</span>&gt;footer&lt;<span class="regexp">/view&gt;</span></span><br><span class="line"><span class="regexp">&lt;/</span>Waterfall&gt;</span><br></pre></td></tr></table></figure>

<p>5、Fragment组件</p>
<p>使用 <code>x-if</code> <code>x-for</code> <code>x-slot</code> 指令时，若不希望产生无意义的元素，我们可以使用 Fragment 组件。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;Fragment x-<span class="keyword">if</span>=&#123;condition&#125;&gt;</span><br><span class="line">  &lt;div /&gt;</span><br><span class="line">&lt;<span class="regexp">/Fragment&gt;</span></span><br></pre></td></tr></table></figure>

<p>6、类名绑定</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//语法</span></span><br><span class="line">&lt;div x-<span class="class"><span class="keyword">class</span></span>=&#123;&#123; <span class="attr">item</span>: <span class="literal">true</span>, <span class="attr">active</span>: val &#125;&#125; /&gt;</span><br><span class="line"><span class="comment">//参考示例如下：</span></span><br><span class="line">&lt;div className=&#123;classnames(&#123; <span class="attr">item</span>: <span class="literal">true</span>, <span class="attr">active</span>: val&#125;)&#125; /&gt;</span><br></pre></td></tr></table></figure>

<h4 id="JSX与HTML属性差异"><a href="#JSX与HTML属性差异" class="headerlink" title="JSX与HTML属性差异"></a>JSX与HTML属性差异</h4><p>className 属性用于指定 CSS 的 class，此特性适用于所有常规 DOM 节点和 SVG 元素，如 <code>&lt;div&gt;</code>，<code>&lt;a&gt;</code> 及其它标签。如果你在 JSX 中使用 Web Components，请使用 class 属性代替</p>
<p>dangerouslySetInnerHTML 是浏览器 DOM 提供 innerHTML 的替换方案。通常来讲，使用代码直接设置 HTML 存在风险，因为很容易无意中使用户暴露于跨站脚本（XSS）的攻击。因此，你可以直接在 JSX 中设置 HTML，但当你想设置 dangerouslySetInnerHTML 时，需要向其传递包含 key 为 <code>__html</code> 的对象，以此来警示你。例如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createMarkup</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;<span class="attr">__html</span>: <span class="string">'First &amp;middot; Second'</span>&#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyComponent</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">dangerouslySetInnerHTML</span>=<span class="string">&#123;createMarkup()&#125;</span> /&gt;</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>由于 for 在 JavaScript 中是保留字，所以 JSX 元素中使用了 htmlFor 来代替。</p>
<h3 id="自定义Hooks"><a href="#自定义Hooks" class="headerlink" title="自定义Hooks"></a>自定义Hooks</h3><p>通过自定义Hooks能够将组件逻辑提取到可重用的函数中。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createElement, useState, useEffect &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> useDocumentTitle = <span class="function"><span class="keyword">function</span>(<span class="params">title</span>) </span>&#123;</span><br><span class="line">  useEffect(</span><br><span class="line">    () =&gt; &#123;</span><br><span class="line">      <span class="built_in">document</span>.title = title;</span><br><span class="line">    &#125;,</span><br><span class="line">    [title]</span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Example</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [count, setCount] = useState(<span class="number">0</span>);</span><br><span class="line">    useDocumentTitle(<span class="string">`You clicked <span class="subst">$&#123;count&#125;</span> times`</span>)</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;p&gt;You clicked &#123;count&#125; times&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">      &lt;button onClick=&#123;() =&gt; setCount(count + 1)&#125;&gt;</span></span><br><span class="line"><span class="regexp">        Click me</span></span><br><span class="line"><span class="regexp">      &lt;/</span>button&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>

<p>上述示例中 useDocumentTitle 是一个自定义 Hooks，可以在其他组件中执行设定 title 的逻辑。如果函数的名字以 “<code>use</code>” 开头并调用其他 Hooks，我们就说这是一个自定义 Hooks。</p>
<h4 id="Hooks与生命周期"><a href="#Hooks与生命周期" class="headerlink" title="Hooks与生命周期"></a>Hooks与生命周期</h4><p>通过以下 Hooks 实现对应的生命周期：</p>
<p>useEffect：1、componentDidUpdate；2、componentDidMount；3、componentWillUnmount。</p>
<p>useLayoutEffect：useEffect 一致，用于获取元素渲染结果。</p>
<p>useMemo、useCallback：shouldComponentUpdate。</p>
<p>使用 useEffect 完成 componentDidUpdate 的效果，示例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//使用useEffect实现的生命周期示例如下：</span></span><br><span class="line"><span class="comment">//下面是使用 useEffect 完成 componentDidUpdate 的效果</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">DemoComponent</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">p</span>&gt;</span>Hello hooks!<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//下面是使用useEffect实现componentDidMount 的功能</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">DemoComponent</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// useEffect 的第二个参数为[]时，表示这个 effect 只会在 componentDidMount、componentWillUnMount 的时候调用。</span></span><br><span class="line">  &#125;, []);</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">p</span>&gt;</span>Hello hooks!<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//下面是使用useEffect实现componentWillUnmount 的功能，</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">DemoComponent</span>(<span class="params">&#123;source&#125;</span>) </span>&#123;</span><br><span class="line">  useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> subscription = source.subscribe();</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">// componentWillUnMount 调用的是第一个参数返回的回调。</span></span><br><span class="line">      <span class="comment">//(一个是调用函数本身，一个是调用useEffect函数返回的回调。)</span></span><br><span class="line">      subscription.unsubscribe();</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;, [source]); <span class="comment">// 表示 source 改变时就是执行一遍</span></span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">p</span>&gt;</span>Hello hooks!<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用memo可以实现shouldComponentUpdate 的功能</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">DemoComponent</span>(<span class="params">props</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>I am &#123;props.name&#125;. hi~<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span>;</span><br><span class="line">&#125;</span><br><span class="line">memo(DemoComponent, (prevProps, nextProps) =&gt; prevProps.name === nextProps.name);</span><br><span class="line"><span class="comment">// 添加第二个参数来指定采用旧 props 和新 props 的自定义比较函数。如果返回 true，则跳过更新。</span></span><br></pre></td></tr></table></figure>

<h2 id="样式方案"><a href="#样式方案" class="headerlink" title="样式方案"></a>样式方案</h2><h3 id="非内联样式CSS"><a href="#非内联样式CSS" class="headerlink" title="非内联样式CSS"></a>非内联样式CSS</h3><p>全局样式：对于整个项目的全局样式，推荐统一定义在<code>src/global.[css|less|scss]</code> 文件中，框架会自动引入该文件：</p>
<p>组件样式：对于页面级和组件级的样式，我们推荐使用 CSS Modules 的方案，这能很好的解决样式开发中全局污染以及命名冲突的问题。</p>
<p>具体规范规则如下：</p>
<ul>
<li>文件名：约定文件名格式为 <code>xxx.module.[css|less|scss]</code></li>
<li>模块化：一个页面或者一个组件对应一个样式文件</li>
</ul>
<p>Home ├── index.module.css └── index.tsx</p>
<p>首先编写样式文件内容；</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** ./pages/Home/index.module.css */</span></span><br><span class="line"><span class="selector-class">.container</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="number">#fff</span>;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">750</span>rpx;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 也可通过 CSS Modules 的 :global 语法定义全局样式 */</span></span><br><span class="line"><span class="selector-pseudo">:global</span> &#123;</span><br><span class="line">  body &#123;</span><br><span class="line">    <span class="selector-tag">a</span> &#123;</span><br><span class="line">      <span class="attribute">color</span>: blue;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>之后在文件中引入对应的样式文件，并将className与对应样式关联：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ./pages/Home/index.tsx</span></span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./index.module.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Home</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;View className=&#123;styles.container&#125;&gt;</span><br><span class="line">      &lt;View&gt;CSS Modules&lt;<span class="regexp">/View&gt;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>View&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">使用该方案之后，上文中的 className 都会被编译为唯一性的名字，避免因为重名 className 而产生样式冲突，如果在浏览器里查看这个示例的 dom 结构，你会发现实际渲染出来是这样的：</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">&lt;View <span class="class"><span class="keyword">class</span></span>=<span class="string">"container--1DTudAN"</span>&gt;title&lt;<span class="regexp">/View&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="内联样式"><a href="#内联样式" class="headerlink" title="内联样式"></a>内联样式</h3><p>如果在 <code>build.json</code> 里配置了 <code>inlineStyle: true</code> 则说明整个项目使用内联样式。对于 Weex、Kraken 这些暂不支持 CSS 的渲染引擎，只支持内联样式（即通过元素的 style 属性设置样式）。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myStyle = &#123;</span><br><span class="line">  fontSize: <span class="string">'24px'</span>,</span><br><span class="line">  color: <span class="string">'#FF0000'</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> element = <span class="xml"><span class="tag">&lt;<span class="name">View</span> <span class="attr">style</span>=<span class="string">&#123;myStyle&#125;</span>&gt;</span>Hello Rax<span class="tag">&lt;/<span class="name">View</span>&gt;</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">同时也支持在 CSS 文件中编写样式，然后通过编译工具最终生成内联的样式，具体使用方式如下：</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">index.css</span></span><br><span class="line"><span class="comment">.container &#123;</span></span><br><span class="line"><span class="comment">  background: #f40;</span></span><br><span class="line"><span class="comment">  padding: 30rpx;</span></span><br><span class="line"><span class="comment">&#125;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">import</span> &#123; createElement &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'./index.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">Title</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (<span class="xml"><span class="tag">&lt;<span class="name">View</span> <span class="attr">className</span>=<span class="string">"container"</span>&gt;</span>...<span class="tag">&lt;/<span class="name">View</span>&gt;</span></span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>同时，我们也支持在使用内联样式方案的项目中局部支持非内联形式，即强制将样式构建为内联时，忽略 <code>src/global.[css|less|scss]</code> 和 <code>*.module.[css|less|scss]</code>，将两者构建为单独的 CSS 文件，用于提供 <code>@keyframes</code> 等 CSS 能力。</p>
<p>在 <code>build.json</code> 中将 <code>inlineStyle</code> 设置为 <code>{ forceEnableCSS: true }</code> 会开启此能力。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//基本用法</span></span><br><span class="line"><span class="keyword">import</span> classNames <span class="keyword">from</span> <span class="string">'classnames'</span>;</span><br><span class="line"><span class="keyword">import</span> cssModule <span class="keyword">from</span> <span class="string">'./index.module.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">App</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// src/global.css 将会被自动引入并构建为单独的 CSS 文件</span></span><br><span class="line">  <span class="comment">// 在此处 'header' 将会引用 src/global.css 中的样式</span></span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&#123;classNames(cssModule.header,</span> '<span class="attr">header</span>')&#125; /&gt;</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//推荐用法</span></span><br><span class="line"><span class="keyword">import</span> cssModule <span class="keyword">from</span> <span class="string">'./index.module.css'</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./index.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">App</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// CSS Modules 以 className 的形式使用</span></span><br><span class="line">  <span class="comment">// inlineStyle 以 style 的形式使用</span></span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&#123;cssModule.header&#125;</span> <span class="attr">style</span>=<span class="string">&#123;styles.header&#125;</span> /&gt;</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//不推荐用法</span></span><br><span class="line"><span class="keyword">import</span> classNames <span class="keyword">from</span> <span class="string">'classnames'</span>;</span><br><span class="line"><span class="keyword">import</span> cssModule <span class="keyword">from</span> <span class="string">'./index.module.css'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'./index.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">App</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// index.css 中的样式不会被内联</span></span><br><span class="line">  <span class="comment">// 在组件中使用了 CSS Module 时 index.css 将会失效</span></span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&#123;classNames(cssModule.header,</span> '<span class="attr">header</span>')&#125; /&gt;</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="进阶指南"><a href="#进阶指南" class="headerlink" title="进阶指南"></a>进阶指南</h3><p>自适应单位rpx：Rax 采用 rpx(responsive pixel) 作为跨端的样式单位，它可以根据屏幕宽度进行自适应。我们规定屏幕宽度为 750rpx，以 iPhone6 为例，它的屏幕宽度为 375px，则 750rpx = 375px = 100vw，所以在 iPhone6 中，1rpx = 0.5px = 100/750vw。</p>
<p>从内联样式迁移到非内联样式：某些 Web 应用可能因为一些历史原因导致使用了内联样式的方案，对于此类应用我·们推荐迁移到非内联样式，这样可以使用更加强大的 CSS 能力</p>
<p>CSS预处理器：Rax App 默认支持了 Less 和 Sass 预处理器，你只需要按照 <code>.less</code> 或 <code>.scss</code> 的规则命名文件即可。如果使用 Sass 你还需要将 sass 的编译器比如社区推荐的 dart-sass：</p>
<p>如果在编辑器中遇到 ts 错误 <code>找不到模块“./index.module.css”或其相应的类型声明</code>，需要在 <code>src/</code> 下新建 <code>typings.d.ts</code> 文件：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/typings.d.ts</span></span><br><span class="line">declare <span class="built_in">module</span> <span class="string">'*.module.css'</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> classes: &#123; [key: string]: string &#125;;</span><br><span class="line">  <span class="keyword">export</span> <span class="keyword">default</span> classes;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Classname压缩细节：该部分是对实验属性 <code>experiments.minifyCSSModules</code> 的实现细节说明。</p>
<p>对于普通的 web 应用，我们使用了 <code>字母</code> + <code>hash</code> 的方式生成 classname，效果如下：</p>
<p>开启前，产物 CSS。</p>
<p>.QueryForm–input–2crH3Qq {} .QueryForm–button–6sdyaqk {}</p>
<p>开启后，产物 CSS</p>
<p>.a2715f34c {} .b2715f34c {}</p>
<p>仔细观察可以发现，上述 classname 中都包含了一个相同的 hash( <code>2715f34c</code> )，同时前面再拼接 a/b/c/…，该方案和 hash:base64:8 一个明显的不同是，我们产出的 classname 更利于 Gzip/Brotli 等算法的压缩，在 Gzip/Brotli 加持下，该策略对于纯 classname，压缩率可以达到 20%~30%（也就是减少 70-80%）。实际项目中由于除了 classname 外，还有样式代码，实际压缩率并不会达到这种程度，但是也可以减少 20% 左右（数据供参考，以实际使用效果为准）</p>
<p>而对于小程序场景，我们使用了另一种压缩策略，效果如下</p>
<p>开启前，产物 CSS</p>
<p>.QueryForm–input–2crH3Qq {} .QueryForm–button–6sdyaqk {}</p>
<p>开启后，产物 CSS</p>
<p>.a-a_mc {} .b-a_mc {}</p>
<p>相较于 Web 应用，在小程序中 hash( <code>2715f34c</code> ) 被替换成了 <code>a</code> 以获得更小的 CSS 体积，同时为了解决和三方 classname 冲突的问题，我们添加了统一的后缀 <code>_mc</code>，对于一些囿于小程序 2M 限制的应用，可以尝试开启该特性。</p>
<h1 id="Rax进阶指南"><a href="#Rax进阶指南" class="headerlink" title="Rax进阶指南"></a>Rax进阶指南</h1><h2 id="框架API"><a href="#框架API" class="headerlink" title="框架API"></a>框架API</h2><h3 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h3><p>rax-app 提供的 API 列表，通常情况下可以通过 <code>rax-app</code> 导入：</p>
<p>runApp：用于创建渲染小程序或 SPA 应用。</p>
<p>APP_MODE：获取应用环境。</p>
<p>ErrorBoundary：用于错误边界的组件。</p>
<p>IAppConfig：appConfig 的类型定义。</p>
<h3 id="状态管理"><a href="#状态管理" class="headerlink" title="状态管理"></a>状态管理</h3><p>可以通过在 build.json 中设置 <code>store: false</code> 关闭状态管理。</p>
<p>store：应用级别的store实例。</p>
<p>IStoreModels：定义模型的类型。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; IStoreModels &#125; <span class="keyword">from</span> <span class="string">'rax-app'</span>;</span><br><span class="line">interface IAppStoreModels extends IStoreModels &#123;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>IStoreDispatch:定义 Dispatch 的类型。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; IStoreDispatch &#125; <span class="keyword">from</span> <span class="string">'rax-app'</span>;</span><br><span class="line"><span class="keyword">const</span> models = &#123;&#125;;</span><br><span class="line"><span class="keyword">export</span> type IRootDispatch = IStoreDispatch&lt;<span class="keyword">typeof</span> models&gt;;</span><br></pre></td></tr></table></figure>

<p>IStoreRootState：定义 RootState 的类型。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; IStoreRootState &#125; <span class="keyword">from</span> <span class="string">'rax-app'</span>; </span><br><span class="line"><span class="keyword">const</span> models = &#123;&#125;;</span><br><span class="line"><span class="keyword">export</span> type IRootState = IStoreRootState&lt;<span class="keyword">typeof</span> models&gt;;</span><br></pre></td></tr></table></figure>

<h3 id="路由"><a href="#路由" class="headerlink" title="路由"></a>路由</h3><p>getHistory：用于获取 history 实例。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; getHistory &#125; <span class="keyword">from</span> <span class="string">'rax-app'</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">HomeButton</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> history = getHistory();</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">handleClick</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    history.push(<span class="string">'/home'</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;button type=<span class="string">'button'</span> onClick=&#123;handleClick&#125;&gt;</span><br><span class="line">      Go home</span><br><span class="line">    &lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>

<p>getSearchParams：用于解析 url 参数。假设当前 URL 为 <code>https://example.com?foo=bar</code>，解析查询参数如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/components/Example</span></span><br><span class="line"><span class="keyword">import</span> &#123; getSearchParams &#125; <span class="keyword">from</span> <span class="string">'rax-app'</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Example</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> searchParams = getSearchParams()</span><br><span class="line">  <span class="comment">// console.log(searchParams); =&gt; &#123; foo: 'bar' &#125;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>withRouter：通过在 Class 组件上添加 <code>withRouter</code> 装饰器，可以获取到路由的 <code>history</code>、<code>location</code>、<code>match</code> 对象。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createElement &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; withRouter &#125; <span class="keyword">from</span> <span class="string">'rax-app'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ShowTheLocation</span>(<span class="params">props</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; history, location &#125; = props;</span><br><span class="line">  <span class="keyword">const</span> handleHistoryPush = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    history.push(<span class="string">'/new-path'</span>);</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;div&gt;当前路径： &#123;location.pathname&#125;&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">      &lt;button onClick=&#123;handleHistoryPush&#125;&gt;点击跳转新页面&lt;/</span>button&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default withRouter(ShowTheLocation);</span></span><br></pre></td></tr></table></figure>

<p>history：获取应用的路由实例</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; history &#125; <span class="keyword">from</span> <span class="string">'rax-app'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于获取 history 栈里的实体个数</span></span><br><span class="line"><span class="built_in">console</span>.log(history.length);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于获取 history 跳转的动作，包含 PUSH、REPLACE 和 POP 三种类型</span></span><br><span class="line"><span class="built_in">console</span>.log(history.action);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于获取 location 对象，包含 pathname、search 和 hash</span></span><br><span class="line"><span class="built_in">console</span>.log(history.location);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于路由跳转</span></span><br><span class="line">history.push(<span class="string">'/home'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于路由替换</span></span><br><span class="line">history.replace(<span class="string">'/home'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于跳转到上一个路由</span></span><br><span class="line">history.goBack();</span><br></pre></td></tr></table></figure>

<h2 id="安全区域适配"><a href="#安全区域适配" class="headerlink" title="安全区域适配"></a>安全区域适配</h2><p>安全区域（Safe Area），指屏幕内不受圆角（corners）、刘海（sensor housing）、底部小黑条（Home Indicator）等元素影响的可视窗口。</p>
<p>对于移动端业务，特别是开启全屏模式的业务（隐藏了浏览器默认的导航栏），为了保证页面的核心内容、可操作区域是在安全区域内，常常需要做安全区域的适配，比如：</p>
<ul>
<li>根据刘海的高度，调整页面主内容距离顶部的高度</li>
<li>根据底部小黑条的高度，调整底部 Bar 的高度</li>
</ul>
<p>WebKit 从 iOS11 起引入一系列 API，来获取安全区域的位置。比如通过环境变量 <code>safe-area-inset-top</code> 可以获取安全区域距离视口顶部的距离，即刘海区域的高度；通过 <code>safe-area-inset-bottom</code> 可以获取安全区域距离视口底部的距离，即小黑条区域的高度。</p>
<h3 id="刘海屏"><a href="#刘海屏" class="headerlink" title="刘海屏"></a>刘海屏</h3><p>为了使页面顶部内容，不被刘海遮挡，可以通过设置容器节点的 padding-top 值来实现，使核心内容整体下移。</p>
<p>获取刘海高度，首先需要设置 <code>viewport-fit</code> ，调整可视窗口的布局方式。当且仅当 <code>viewport-fit</code> 设置为 <code>cover</code> 时，可以进一步设置页面的安全区域范围。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;meta name=<span class="string">"viewport"</span> content=<span class="string">"width=device-width, viewport-fit=cover"</span>&gt;</span><br><span class="line"><span class="comment">//然后，结合 env() 方法，可以获取 safe-area-inset-top 值，并将其作为容器节点的 padding-top 值。</span></span><br><span class="line">.root &#123;</span><br><span class="line">  padding-top: constant(safe-area-inset-top); <span class="comment">/* 兼容  iOS &lt; 11.2 */</span></span><br><span class="line">  padding-top: env(safe-area-inset-top); <span class="comment">// /* iOS &gt; 11.2 */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="小黑条适配"><a href="#小黑条适配" class="headerlink" title="小黑条适配"></a>小黑条适配</h3><p>对于有 tabbar 的应用，iPhone 底部的小黑条常常会挡住 tabbar，影响其可操作区域。和刘海屏适配的原理一致，以 tabbar 为例，小黑条适配可以通过调整 tabbar 的 padding-bottom 值，增加空白区域来实现。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*通过设置 tabbar 的 padding 值来调整安全区域*/</span></span><br><span class="line"><span class="selector-class">.tabbar</span> &#123;</span><br><span class="line">  <span class="attribute">padding-bottom</span>: <span class="number">0</span>; <span class="comment">/* 无小黑条的情况下，无需额外设置  */</span></span><br><span class="line">  <span class="attribute">padding-bottom</span>: <span class="built_in">constant</span>(safe-area-inset-bottom); <span class="comment">/* 兼容  iOS &lt; 11.2 */</span></span><br><span class="line">  padding-bottom: env(safe-area-inset-bottom); // /* iOS &gt; 11.2 */</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="动画方案"><a href="#动画方案" class="headerlink" title="动画方案"></a>动画方案</h2><p>对于基础的动效，推荐的做法是结合 CSS 属性 transition 或 animation 实现。</p>
<h3 id="Transitions"><a href="#Transitions" class="headerlink" title="Transitions"></a>Transitions</h3><p>CSS <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions" target="_blank" rel="noopener">transitions</a> 提供了一种在更改CSS属性时控制动画速度的方法。它可以让属性变化成为一个持续一段时间的过程，而不是立即生效的。比如，将一个元素的颜色从白色改为黑色，通常这个改变是立即生效的，使用 CSS transitions 后该元素的颜色将逐渐从白色变为黑色，按照一定的曲线速率变化，这个过程可以自定义。</p>
<p>示例：当 hover 到节点时，节点的宽高、背景色及 transform 值发生了渐变。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* index.module.css */</span></span><br><span class="line"><span class="selector-class">.box</span> &#123;</span><br><span class="line">  <span class="attribute">border-style</span>: solid;</span><br><span class="line">  <span class="attribute">border-width</span>: <span class="number">1px</span>;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">100</span>rpx;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">100</span>rpx;</span><br><span class="line">  <span class="attribute">background-color</span>: <span class="number">#0000FF</span>;</span><br><span class="line">  <span class="attribute">-webkit-transition</span>:width <span class="number">2s</span>, height <span class="number">2s</span>,</span><br><span class="line">      background-color <span class="number">2s</span>, -webkit-transform <span class="number">2s</span>;</span><br><span class="line">  <span class="attribute">transition</span>:width <span class="number">2s</span>, height <span class="number">2s</span>, background-color <span class="number">2s</span>, transform <span class="number">2s</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.box</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">background-color</span>: <span class="number">#FFCCCC</span>;</span><br><span class="line">  <span class="attribute">width</span>:<span class="number">200</span>rpx;</span><br><span class="line">  <span class="attribute">height</span>:<span class="number">200</span>rpx;</span><br><span class="line">  <span class="attribute">-webkit-transform</span>:<span class="built_in">rotate</span>(<span class="number">180deg</span>);</span><br><span class="line">  <span class="attribute">transform</span>:<span class="built_in">rotate</span>(<span class="number">180deg</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.jsx</span></span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./index.module.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&#123;styles.box&#125;</span> /&gt;</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h3 id="Animations"><a href="#Animations" class="headerlink" title="Animations"></a>Animations</h3><p>CSS <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Animations/Using_CSS_animations" target="_blank" rel="noopener">animations</a> 提供了通过 <code>关键帧</code> 创建简单动画的能力。通过使用 @keyframes 指定动画开始、结束以及中间点的关键帧，在每一帧上描述了动画元素在给定的时间点上应该如何渲染（CSS样式配置）， 就能够非常容易地创建动画序列。</p>
<p>示例：通过 animation 实现节点显示时，从屏幕右侧划入的效果。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* index.module.css */</span></span><br><span class="line"><span class="selector-class">.box</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">100</span>rpx;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">100</span>rpx;</span><br><span class="line">  <span class="attribute">background-color</span>: <span class="number">#0000FF</span>;</span><br><span class="line">  <span class="attribute">animation-duration</span>: <span class="number">3s</span>;</span><br><span class="line">  <span class="attribute">animation-name</span>: slidein;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@keyframes</span> slidein &#123;</span><br><span class="line">  <span class="selector-tag">from</span> &#123;</span><br><span class="line">    <span class="attribute">margin-left</span>: <span class="number">100%</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="selector-tag">to</span> &#123;</span><br><span class="line">    <span class="attribute">margin-left</span>: <span class="number">0%</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.jsx</span></span><br><span class="line"><span class="keyword">import</span> &#123; createElement, useState &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"><span class="keyword">import</span> View <span class="keyword">from</span> <span class="string">'rax-view'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./index.module.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">Home</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [showBox, setBox] = useState(<span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">onClick</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    setBox(<span class="literal">true</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;View&gt;</span><br><span class="line">      &lt;View className=&#123;styles.buttom&#125; onClick=&#123;onClick&#125;&gt;show box&lt;<span class="regexp">/View&gt;</span></span><br><span class="line"><span class="regexp">      &#123;</span></span><br><span class="line"><span class="regexp">        showBox &amp;&amp; &lt;View className=&#123;styles.box&#125;&gt;&lt;/</span>View&gt;</span><br><span class="line">      &#125;</span><br><span class="line">    &lt;<span class="regexp">/View&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>

<h3 id="静态资源"><a href="#静态资源" class="headerlink" title="静态资源"></a>静态资源</h3><p><code>src/app.json</code> 用于对应用进行全局配置，设置路由、窗口表现、渲染方式等。默认配置示例如下；可根据具体场景进行配置使用：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"routes"</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">"path"</span>: <span class="string">"/"</span>,</span><br><span class="line">      <span class="attr">"source"</span>: <span class="string">"pages/Home/index"</span></span><br><span class="line">    &#125;</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">"window"</span>: &#123;</span><br><span class="line">    <span class="attr">"title"</span>: <span class="string">"Rax App 1.0"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="静态资源不构建"><a href="#静态资源不构建" class="headerlink" title="静态资源不构建"></a>静态资源不构建</h4><p>你可以将无需经过 webpack 构建的静态资源添加到 <code>public</code> 文件夹。</p>
<p>如果将文件放入 <code>public</code> 文件夹，webpack 将 <strong>不会</strong> 处理它。而是它将被复制到构建文件夹中。要引用 <code>public</code> 文件夹中的资源，需要使用名为 <code>process.env.PUBLIC_URL</code> 的特殊变量，这个值会根据工程配中的 <code>publicPath</code> 变化：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">render() &#123;</span><br><span class="line">  <span class="comment">// 注意：这是一个 escape hatch，应该谨慎使用！</span></span><br><span class="line">  <span class="comment">// 通常我们建议使用`import`来获取资源的 URL </span></span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&#123;process.env.PUBLIC_URL</span> + '/<span class="attr">img</span>/<span class="attr">logo.png</span>'&#125; /&gt;</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意这种方法的缺点：</p>
<ul>
<li><code>public</code> 文件夹中的所有文件都不会进行后处理或压缩</li>
<li>在编译时不会调用丢失的文件，并且会导致用户出现 404 错误</li>
<li>构建产物的文件名不包含内容哈希值，因此你需要添加查询参数或在每次更改时重命名它们（以便清除浏览器缓存）</li>
</ul>
<p>一般情况下，我们建议你在 JavaScript 文件中 <code>import</code> 资源，这种机制提供了许多好处：</p>
<ul>
<li>脚本和样式被压缩并打包在一起，以避免额外的网络请求</li>
<li>缺少文件会导致编译错误，而不是给用户 404 错误</li>
<li>构建产物的文件名包含内容哈希，因此你无需担心浏览器会缓存旧版本</li>
</ul>
<h4 id="何时使用public"><a href="#何时使用public" class="headerlink" title="何时使用public"></a>何时使用public</h4><p>通常我们建议从 JavaScript 导入 stylesheets，图片和字体。 <code>public</code> 文件夹可用作许多不常见情况的变通方法：</p>
<ul>
<li>你需要在构建输出中具有特定名称的文件，例如 <code>manifest.json</code></li>
<li>你有数千张图片，需要动态引用它们的路径</li>
<li>你希望在打包代码之外包含一个无需走构建逻辑的小脚本</li>
<li>在工程中，某些库可能与 webpack 不兼容，你没有其他选择，只能将其放在 <code>public</code> 中引入</li>
</ul>
<h3 id="代码质量保障"><a href="#代码质量保障" class="headerlink" title="代码质量保障"></a>代码质量保障</h3><p>为了保证代码质量，我们推荐使用 lint 相关的工具对代码进行检测，同时为了降低常规 lint 工具的使用成本，我们封装了 <a href="https://github.com/ice-lab/spec" target="_blank" rel="noopener">@iceworks/spec</a> 这个 npm 包，基础的 eslint 规则与<a href="https://f2e.alibaba-inc.com/specification/" target="_blank" rel="noopener">阿里巴巴前端规范</a>保持一致。</p>
<h4 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//eslint使用方法如下</span></span><br><span class="line"><span class="comment">// .eslintrc.js。JavaScript工程</span></span><br><span class="line"><span class="keyword">const</span> &#123; getESLintConfig &#125; = <span class="built_in">require</span>(<span class="string">'@iceworks/spec'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = getESLintConfig(<span class="string">'rax'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// .eslintrc.js。TypeScript工程</span></span><br><span class="line"><span class="keyword">const</span> &#123; getESLintConfig &#125; = <span class="built_in">require</span>(<span class="string">'@iceworks/spec'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = getESLintConfig(<span class="string">'rax-ts'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//stylelint 用来检测样式代码的风格，新建配置文件 .stylelintrc.js 引入 lint 规则：</span></span><br><span class="line"><span class="comment">// .stylelintrc.js</span></span><br><span class="line"><span class="keyword">const</span> &#123; getStylelintConfig &#125; = <span class="built_in">require</span>(<span class="string">'@iceworks/spec'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = getStylelintConfig(<span class="string">'rax'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//用于规范 commit message 的规范，防止全是 fix 这种无意义的 commit message 导致历史记录追溯比较麻烦，新建配置文件 .commitlintrc.js 引入规则：</span></span><br><span class="line"><span class="comment">// .commitlintrc.js</span></span><br><span class="line"><span class="keyword">const</span> &#123; getCommitlintConfig &#125; = <span class="built_in">require</span>(<span class="string">'@iceworks/spec'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = getCommitlintConfig(<span class="string">'rax'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// package.json</span></span><br><span class="line"><span class="string">"scripts"</span>: &#123;</span><br><span class="line">  <span class="string">"lint"</span>: <span class="string">"npm run eslint &amp;&amp; npm run stylelint"</span>,</span><br><span class="line">  <span class="string">"eslint"</span>: <span class="string">"eslint --cache --ext .js,.jsx,.ts,.tsx ./"</span>,</span><br><span class="line">  <span class="string">"stylelint"</span>: <span class="string">"stylelint **/*.&#123;css,scss,less&#125;"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过 npm scripts 配置命令：这样就可以通过npm run lint来运行lint任务了。</p>
<p>如果遇到 js 项目 ESLint 问题可以实时反馈而 ts 项目无效时，可以打开 VS Code setting ，在搜索框中输入 ‘ eslint validate ’ ，勾选相关选项。</p>
<h4 id="迁移"><a href="#迁移" class="headerlink" title="迁移"></a>迁移</h4><p>从自定义配置迁移</p>
<ol>
<li>移除项目中的各类 ESLint plugin config 及 parser。</li>
<li>安装 @iceworks/spec。</li>
<li>参考上述文档修改 lint 配置。</li>
</ol>
<p>**注：一定要清除项目之前的 ESLint 的 **<strong>plugin config 及 parser 相关包。</strong></p>
<h2 id="代码分割"><a href="#代码分割" class="headerlink" title="代码分割"></a>代码分割</h2><p>代码切割（Code Splitting） 能够把代码切割到不同的 bundle 中，实现按需或并行加载。代码切割可以用于获取更小的 bundle，以及控制资源加载优先级，如果使用合理，会极大减少首屏资源加载时间。</p>
<h3 id="dynamic-import"><a href="#dynamic-import" class="headerlink" title="dynamic import"></a>dynamic import</h3><p>使用 <code>import()</code>，webpack 会在编译阶段对引入的资源进行代码切割，即只有当运行时逻辑执行到 <code>import()</code> 调用点时才会加载对应的资源，该函数返回值是 <code>Promise</code>，开发者可以在链式回调中获取到引用的模块资源。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; isWeb &#125; <span class="keyword">from</span> <span class="string">'@uni/env'</span>;</span><br><span class="line"><span class="keyword">if</span> (isWeb) &#123;</span><br><span class="line">  <span class="keyword">import</span>(<span class="string">'./fetch'</span>).then(<span class="function"><span class="params">fetch</span> =&gt;</span> &#123;</span><br><span class="line">    fetch(<span class="string">'m.taobao.com'</span>);</span><br><span class="line">  &#125;).catch(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.error(<span class="string">'模块引入失败！'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="rax-use-import"><a href="#rax-use-import" class="headerlink" title="rax-use-import"></a>rax-use-import</h3><p>函数式组件本身不能是一个异步函数，所以在使用 <code>import()</code> 动态加载子组件时会存在异步更新视图的处理，为了简化对应场景，我们提供了 <code>rax-use-import</code> 可以在函数组件中用同步的写法快速使用动态加载子组件的能力。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createElement &#125; <span class="keyword">from</span> <span class="string">'rax'</span>;</span><br><span class="line"><span class="keyword">import</span> useImport <span class="keyword">from</span> <span class="string">'rax-use-import'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">App</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [Bar, error] = useImport(<span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">import</span>(<span class="comment">/* webpackChunkName: "bar" */</span> <span class="string">'./Bar'</span>));</span><br><span class="line">  <span class="keyword">if</span> (error) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">p</span>&gt;</span>error<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (Bar) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">Bar</span> /&gt;</span></span></span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">p</span>&gt;</span>loading<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span>;  </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="插件开发"><a href="#插件开发" class="headerlink" title="插件开发"></a>插件开发</h2><p>rax-app 基于工程构建工具 build-scripts，提供了丰富的插件用于提升项目的开发体验。</p>
<p>rax-app 基于工程构建工具 build-scripts 封装，因此在插件能力上也完整继承了 build-scripts。除了通过插件定制工程能力以外，rax-app 还为插件扩展了运行时定制的能力，这让插件拥有更多的想象空间。</p>
<p>插件机制是 rax-app 的核心之一，当前 rax-app 的基础能力都是通过插件来实现。插件机制不但可以保证框架核心足够精简和稳定，还可以通过插件对运行时和编译时的能力进行封装复用，最终打造一个完整的生态。</p>
<h3 id="plugin-rax-compat-react"><a href="#plugin-rax-compat-react" class="headerlink" title="plugin-rax-compat-react"></a>plugin-rax-compat-react</h3><p>用于在 Rax 项目里兼容使用 React 组件。在 Rax App 项目里，当没有设置 <code>store: false</code> 时，默认就已经做了相关兼容处理，无需再添加该插件。</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"plugins"</span>: [</span><br><span class="line">    <span class="string">"build-plugin-rax-compat-react"</span></span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="初始化插件"><a href="#初始化插件" class="headerlink" title="初始化插件"></a>初始化插件</h3><p>在项目根目录下新建 <code>build.plugin.js</code> 文件，然后写入以下代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// build.plugin.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123; context, onGetWebpackConfig &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  onGetWebpackConfig(<span class="function">(<span class="params">config</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'Get Webpack Config'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//然后再build.json里引入自定义插件即可，在plugin数组中添加上即可。</span></span><br><span class="line"><span class="comment">//如果自定义逻辑比较复杂且需要跨项目使用，推荐将插件发布为一个 npm 包</span></span><br></pre></td></tr></table></figure>

<p>插件入口是一个fucntion，代码如下：</p>
<p>在执行 start 或者 build 命令时会按照顺序调用每个插件的入口方法。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123; context, onGetWebpackConfig, ...restApi &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  onGetWebpackConfig(<span class="function">(<span class="params">config</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'Get Webpack Config'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="插件API"><a href="#插件API" class="headerlink" title="插件API"></a>插件API</h3><p>插件入口方法接受的参数即插件 API，API 列表如下：</p>
<p>1、context：包含运行时的各种环境信息。</p>
<p>command：当前运行命令，如start、build、test；commandArgs<code> script 命令执行时接受到的参数；</code>rootDir<code> 项目根目录；</code>userConfig<code> 用户在 build.json 中配置的内容；</code>pkg`项目 package.json 中的内容</p>
<p>2、onGetWebpackConfig：通过 <code>onGetWebpackConfig</code> 获取 webpack-chain 形式的配置，并对配置进行自定义修改：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;onGetWebpackConfig, registerTask&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  registerTask(<span class="string">'default'</span>, webpackConfig);</span><br><span class="line"></span><br><span class="line">  onGetWebpackConfig(<span class="function">(<span class="params">config</span>) =&gt;</span> &#123;</span><br><span class="line">    config.entry(<span class="string">'xxx'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>3、onGetJestConfig：通过 <code>onGetJestConfig</code> 获取 jest 配置，可对配置进行自定义修改：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;onGetJestConfig&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  onGetJestConfig(<span class="function">(<span class="params">jestConfig</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> modifiedJestConfig = modify(jestConfig);</span><br><span class="line">      <span class="keyword">return</span> modifiedJestConfig;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>4、onHook：通过 onHook 监听命令运行时事件，onHook 注册的函数执行完成后才会执行后续操作，可以用于在命令运行中途插入插件想做的操作：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123; onHook &#125;</span>) =&gt;</span> &#123;</span><br><span class="line"> onHook(<span class="string">'before.build.load'</span>, () =&gt; &#123;</span><br><span class="line">   <span class="comment">// do something before dev</span></span><br><span class="line"> &#125;);</span><br><span class="line"> onHook(<span class="string">'after.build.compile'</span>, (stats) =&gt; &#123;</span><br><span class="line">   <span class="comment">// do something after build</span></span><br><span class="line"> &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>目前支持的命令执行生命周期如下：</p>
<p><img src="/../../image/Rax%E5%88%9D%E5%AD%A6/start.png" alt="start"></p>
<p><img src="/../../image/Rax%E5%88%9D%E5%AD%A6/test.png" alt="test"><img src="/../../image/Rax%E5%88%9D%E5%AD%A6/build.png" alt="build"></p>
<p>5、log：统一的 log 工具，底层使用 <code>npmlog</code> ，便于生成统一格式的 log。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">log.info(<span class="string">'start'</span>);</span><br><span class="line">log.verbose(<span class="string">'debug'</span>);</span><br><span class="line">log.error(<span class="string">'exit'</span>);</span><br></pre></td></tr></table></figure>

<p>6、registerUserConfig：为用户配置文件 <code>build.json</code> 中添加自定义字段。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;registerUserConfig&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  registerUserConfig(&#123;</span><br><span class="line">    name: <span class="string">'custom-key'</span>,</span><br><span class="line">    validation: <span class="string">'boolean'</span> <span class="comment">// 可选，支持类型有 string, number, array, object, boolean</span></span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>7、registerCliOption：为命令行启动添加自定义参数。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;registerCliOption&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  registerCliOption(&#123;</span><br><span class="line">    name: <span class="string">'custom-option'</span>, <span class="comment">// 参数名</span></span><br><span class="line">    commands: [<span class="string">'start'</span>], <span class="comment">// 命令</span></span><br><span class="line">    configWebpack: <span class="function">(<span class="params">arg</span>) =&gt;</span> &#123;&#125; <span class="comment">// 可选，arg 为命令行参数对应值</span></span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>8、registerMethod：注册自定义方法。通过 <code>applyMethod</code> 调用。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;registerMethod&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  registerMethod(name, func); <span class="comment">// name, func 分别为方法名和方法</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>9、modifyUserConfig：修改用户配置文件。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;modifyUserConfig&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  modifyUserConfig(key, value); <span class="comment">// key, value 分别为用户配置文件键值对</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>10、registerTask：添加 webpack 配置，配置为 webpack-chain 形式。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;registerTask&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  registerTask(name, config); <span class="comment">// name: Task名, config: webpack-chain 形式的配置</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>11、getAllTask：获取所有 webpack 配置名称。getAllPlugin：获取所有插件。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;getAllTask&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> alltasks = getAlltask();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;getAllPlugin&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 获取所有插件数组</span></span><br><span class="line">  <span class="comment">// 类型：() =&gt; [&#123;pluginPath, options, name&#125;]</span></span><br><span class="line">  <span class="keyword">const</span> plugins = getAllPlugin(); ，[]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="扩展API"><a href="#扩展API" class="headerlink" title="扩展API"></a>扩展API</h3><p>除了以上由 build-scripts 内置支持的 API，我们还通过 rax-app 对插件 API 做了扩展，扩展的 API 需要通过以下方式调用：</p>
<p>目前扩展的 API 仅支持同步调用。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123; applyMethod &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 第一个参数对应 API 名称，第二个参数对应 API 参数</span></span><br><span class="line">  applyMethod(<span class="string">'addExport'</span>, &#123; <span class="attr">source</span>: <span class="string">`./config`</span>, exportName &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>addExport</p>
<p>向 <code>rax-app</code> 里注册模块，实现 <code>import { foo } from &#39;rax-app&#39;;</code> 的能力：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 实现 import &#123; request &#125; from 'rax-app'; request 由插件的 `./request/request` 文件实现</span></span><br><span class="line">applyMethod(<span class="string">'addExport'</span>, &#123; <span class="attr">source</span>: <span class="string">'./request/request'</span>, <span class="attr">exportName</span>: <span class="string">'request'</span> &#125;)</span><br></pre></td></tr></table></figure>

<p>removeExport</p>
<p>与 <code>addExport</code> 对应：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">this</span>.applyMethod(<span class="string">'removeExport'</span>, <span class="string">'store'</span>);</span><br></pre></td></tr></table></figure>

<p>addAppConfigTypes</p>
<p>向 appConfig 添加类型</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 第一项参数对应 API 名称，第二项参数对应 API 参数。</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// API 参数：</span></span><br><span class="line"><span class="comment">// source: 类型声明文件。./foo/types，对应 TEMP_DIR/foo/types。 TEMP_DIR，可通过 getValue('TEMP_PATH') 获得。注意：需先将对应类型文件移至 TEMP_DIR。</span></span><br><span class="line"><span class="comment">// specifier: 导出类型标识符，可选，默认值为 '*'。</span></span><br><span class="line"><span class="comment">// exportName: 添加至 appConfig 类型 IAppConfig 上的导出名。</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 结果为：</span></span><br><span class="line"><span class="comment">// // TEMP_DIR/types.ts</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// import &#123; Foo &#125; from './foo/types';</span></span><br><span class="line"><span class="comment">// export interface IAppConfig &#123;</span></span><br><span class="line"><span class="comment">//   foo?: Foo</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br><span class="line">applyMethod(<span class="string">'addAppConfigTypes'</span>, &#123; <span class="attr">source</span>: <span class="string">`./foo/types`</span>, <span class="attr">specifier</span>: <span class="string">'&#123; Foo &#125;'</span>, <span class="attr">exportName</span>: <span class="string">`foo?: Foo`</span> &#125;);</span><br></pre></td></tr></table></figure>

<p>removeAppConfigTypes</p>
<p>与 <code>addAppConfigTypes</code> 对应</p>
<p>getPages</p>
<p>获取 <code>src/pages</code> 下的一级页面列表：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ['Home', 'About']</span></span><br><span class="line"><span class="keyword">const</span> pages = <span class="keyword">this</span>.applyMethod(<span class="string">'getPages'</span>, <span class="keyword">this</span>.rootDir);</span><br></pre></td></tr></table></figure>

<p>watchFileChange</p>
<p>监听 <code>src</code> 下的文件变化：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">applyMethod(<span class="string">'watchFileChange'</span>, <span class="string">'src/config.*'</span>, <span class="keyword">async</span> (event: string) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span> (event === <span class="string">'unlink'</span> || event === <span class="string">'add'</span>) &#123;</span><br><span class="line">    <span class="comment">// do something</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h2 id="插件参数"><a href="#插件参数" class="headerlink" title="插件参数"></a>插件参数</h2><p>用户可以在 <code>build.json</code> 中指定插件参数：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"plugins"</span>: [</span><br><span class="line">    [<span class="string">"build-plugin-rax-example"</span>, &#123;</span><br><span class="line">      <span class="attr">"type"</span>: <span class="string">"bar"</span></span><br><span class="line">    &#125;]</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>插件入口函数的第二个参数即用户自定义配置：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123; context, log &#125;, options</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; type &#125; = options;</span><br><span class="line">  log.info(type); <span class="comment">// =&gt; bar</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="插件通信"><a href="#插件通信" class="headerlink" title="插件通信"></a>插件通信</h2><p>插件间需要进行通信的场景诉求：</p>
<ol>
<li>不同插件之间需要知道彼此的存在来确定是否执行相应的逻辑</li>
<li>多个插件共有的配置信息可以抽出来，在某个插件中进行配置</li>
</ol>
<p>使用 <code>setValue</code> 和 <code>getValue</code> 两个API来实现，分别用于数据的存取。</p>
<h3 id="setValue"><a href="#setValue" class="headerlink" title="setValue"></a>setValue</h3><p>类型：<code>(key: string | number, value: any) =&gt; void</code>，示例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// build-plugin-test</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123; setValue &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  setValue(<span class="string">'key'</span>, <span class="number">123</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="getValue"><a href="#getValue" class="headerlink" title="getValue"></a>getValue</h3><p>类型：<code>(key: string | number) =&gt; any</code>，示例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">&#123;getValue&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> value = getValue(<span class="string">'key'</span>); <span class="comment">// 123</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>同时在 rax-app 中也内置了几个变量：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> projectType = getValue(<span class="string">'PROJECT_TYPE'</span>); <span class="comment">// ts|js</span></span><br><span class="line"><span class="keyword">const</span> dirPath = getValue(<span class="string">'TEMP_PATH'</span>);  <span class="comment">// 对应 .rax 的路径</span></span><br></pre></td></tr></table></figure>

<h2 id="类型"><a href="#类型" class="headerlink" title="类型"></a>类型</h2><p>接口类型通过以下方法引入：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; IPlugin &#125; <span class="keyword">from</span> <span class="string">'@alib/build-scripts'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> plugin: IPlugin = <span class="keyword">async</span> (&#123; getValue, applyMethod, onGetWebpackConfig &#125;): <span class="built_in">Promise</span>&lt;<span class="keyword">void</span>&gt; =&gt; &#123;</span><br><span class="line">  onGetWebpackConfig(<span class="function">(<span class="params">config</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> plugin;</span><br></pre></td></tr></table></figure>

    </div>
    
    
    
    
    <div>
      
        <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    
</div>

      
    </div>
        <div class="reward-container">
  <div>坚持原创技术分享，您的支持将鼓励我继续创作！</div>
  <button onclick="var qr = document.getElementById('qr'); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
      
      <div style="display: inline-block;">
        <img src="/images/alipay.png" alt="Li Yudong 支付宝">
        <p>支付宝</p>
      </div>

  </div>
</div>

        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>Li Yudong
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="http://example.com/2021/11/20/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Rax%E5%88%9D%E5%AD%A6/" title="Rax初学">http://example.com/2021/11/20/盒马技术栈初学/Rax初学/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>

    
      <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/%E5%85%AC%E5%8F%B8%E6%8A%80%E6%9C%AF%E6%A0%88/" rel="tag"># 公司技术栈</a>
          </div>
    
        

    
        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2021/11/16/react/react%E4%BF%AE%E7%82%BC(%E5%85%AD)-Webpack%E9%85%8D%E7%BD%AE/" rel="prev" title="react修炼（六）-Webpack配置">
      <i class="fa fa-chevron-left"></i> react修炼（六）-Webpack配置
    </a></div>
      <div class="post-nav-item">
    <a href="/2021/11/22/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/RaxWEB%E5%BA%94%E7%94%A8/" rel="next" title="RaxWEB应用">
      RaxWEB应用 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#前言"><span class="nav-number">1.</span> <span class="nav-text">前言</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#与React区别"><span class="nav-number">1.1.</span> <span class="nav-text">与React区别</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#修改写法迁移"><span class="nav-number">1.1.1.</span> <span class="nav-text">修改写法迁移</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Rax基础指南"><span class="nav-number">2.</span> <span class="nav-text">Rax基础指南</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#组件编写"><span class="nav-number">2.1.</span> <span class="nav-text">组件编写</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Component"><span class="nav-number">2.1.1.</span> <span class="nav-text">Component</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Props"><span class="nav-number">2.1.1.1.</span> <span class="nav-text">Props</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#State"><span class="nav-number">2.1.1.2.</span> <span class="nav-text">State</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Fragment"><span class="nav-number">2.1.1.3.</span> <span class="nav-text">Fragment</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Hooks"><span class="nav-number">2.1.2.</span> <span class="nav-text">Hooks</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#useState"><span class="nav-number">2.1.2.1.</span> <span class="nav-text">useState</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#useEffect"><span class="nav-number">2.1.2.2.</span> <span class="nav-text">useEffect</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Hooks-使用规则"><span class="nav-number">2.1.2.3.</span> <span class="nav-text">Hooks 使用规则</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#JSX-语法"><span class="nav-number">2.1.3.</span> <span class="nav-text">JSX+语法</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#JSX与HTML属性差异"><span class="nav-number">2.1.3.1.</span> <span class="nav-text">JSX与HTML属性差异</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#自定义Hooks"><span class="nav-number">2.1.4.</span> <span class="nav-text">自定义Hooks</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Hooks与生命周期"><span class="nav-number">2.1.4.1.</span> <span class="nav-text">Hooks与生命周期</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#样式方案"><span class="nav-number">2.2.</span> <span class="nav-text">样式方案</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#非内联样式CSS"><span class="nav-number">2.2.1.</span> <span class="nav-text">非内联样式CSS</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#内联样式"><span class="nav-number">2.2.2.</span> <span class="nav-text">内联样式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#进阶指南"><span class="nav-number">2.2.3.</span> <span class="nav-text">进阶指南</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Rax进阶指南"><span class="nav-number">3.</span> <span class="nav-text">Rax进阶指南</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#框架API"><span class="nav-number">3.1.</span> <span class="nav-text">框架API</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#基础"><span class="nav-number">3.1.1.</span> <span class="nav-text">基础</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#状态管理"><span class="nav-number">3.1.2.</span> <span class="nav-text">状态管理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#路由"><span class="nav-number">3.1.3.</span> <span class="nav-text">路由</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#安全区域适配"><span class="nav-number">3.2.</span> <span class="nav-text">安全区域适配</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#刘海屏"><span class="nav-number">3.2.1.</span> <span class="nav-text">刘海屏</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#小黑条适配"><span class="nav-number">3.2.2.</span> <span class="nav-text">小黑条适配</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#动画方案"><span class="nav-number">3.3.</span> <span class="nav-text">动画方案</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Transitions"><span class="nav-number">3.3.1.</span> <span class="nav-text">Transitions</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Animations"><span class="nav-number">3.3.2.</span> <span class="nav-text">Animations</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#静态资源"><span class="nav-number">3.3.3.</span> <span class="nav-text">静态资源</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#静态资源不构建"><span class="nav-number">3.3.3.1.</span> <span class="nav-text">静态资源不构建</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#何时使用public"><span class="nav-number">3.3.3.2.</span> <span class="nav-text">何时使用public</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#代码质量保障"><span class="nav-number">3.3.4.</span> <span class="nav-text">代码质量保障</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#配置文件"><span class="nav-number">3.3.4.1.</span> <span class="nav-text">配置文件</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#迁移"><span class="nav-number">3.3.4.2.</span> <span class="nav-text">迁移</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#代码分割"><span class="nav-number">3.4.</span> <span class="nav-text">代码分割</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#dynamic-import"><span class="nav-number">3.4.1.</span> <span class="nav-text">dynamic import</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#rax-use-import"><span class="nav-number">3.4.2.</span> <span class="nav-text">rax-use-import</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#插件开发"><span class="nav-number">3.5.</span> <span class="nav-text">插件开发</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#plugin-rax-compat-react"><span class="nav-number">3.5.1.</span> <span class="nav-text">plugin-rax-compat-react</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#初始化插件"><span class="nav-number">3.5.2.</span> <span class="nav-text">初始化插件</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#插件API"><span class="nav-number">3.5.3.</span> <span class="nav-text">插件API</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#扩展API"><span class="nav-number">3.5.4.</span> <span class="nav-text">扩展API</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#插件参数"><span class="nav-number">3.6.</span> <span class="nav-text">插件参数</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#插件通信"><span class="nav-number">3.7.</span> <span class="nav-text">插件通信</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#setValue"><span class="nav-number">3.7.1.</span> <span class="nav-text">setValue</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#getValue"><span class="nav-number">3.7.2.</span> <span class="nav-text">getValue</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#类型"><span class="nav-number">3.8.</span> <span class="nav-text">类型</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Li Yudong"
      src="/images/author.jpg">
  <p class="site-author-name" itemprop="name">Li Yudong</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">95</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">14</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">18</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/tiarmor1" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;tiarmor1" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:1157019137@qq.com" title="E-Mail → mailto:1157019137@qq.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>
  <div class="cc-license motion-element" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" rel="noopener" target="_blank"><img src="/images/cc-by-nc-sa.svg" alt="Creative Commons"></a>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 2020 – 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Li Yudong</span>
</div>

        








      </div>
    </footer>
  </div>

  
  
  <script color='0,0,0' opacity='0.5' zIndex='-1' count='150' src="/lib1/canvas-nest/canvas-nest.min.js"></script>
  <script src="/lib1/anime.min.js"></script>
  <script src="/lib1/velocity/velocity.min.js"></script>
  <script src="/lib1/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  




  
<script src="/js/local-search.js"></script>













  

  

  
   <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> 
   <script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
   <script type="text/javascript" src="/js/fireworks.js"></script>
  
</body>
</html>

